#include "storage/disk_manager.h"
#include <iostream>
#include <cstring>

DiskManager::DiskManager(const std::string& dbFile)
    : dbFile(dbFile) {
    file.open(dbFile, std::ios::in | std::ios::out | std::ios::binary);

    if (!file.is_open()) {
        // File doesn't exist, create it
        file.open(dbFile, std::ios::out | std::ios::binary);
        file.close();
        file.open(dbFile, std::ios::in | std::ios::out | std::ios::binary);
    }

    // Initialize page allocation map
    file.seekg(0, std::ios::end);
    size_t fileSize = file.tellg();
    uint32_t numPages = fileSize / PAGE_SIZE;

    for (uint32_t i = 0; i < numPages; ++i) {
        pageAllocationMap[i] = true;
    }
}

DiskManager::~DiskManager() {
    if (file.is_open()) {
        file.close();
    }
}

void DiskManager::readPage(uint32_t pageId, char* data) {
    file.seekg(pageId * PAGE_SIZE, std::ios::beg);
    file.read(data, PAGE_SIZE);

    if (file.fail()) {
        throw std::runtime_error("Failed to read page " + std::to_string(pageId));
    }
}

void DiskManager::writePage(uint32_t pageId, const char* data) {
    ensureFileSize(pageId);
    file.seekp(pageId * PAGE_SIZE, std::ios::beg);
    file.write(data, PAGE_SIZE);
    file.flush();

    if (file.fail()) {
        throw std::runtime_error("Failed to write page " + std::to_string(pageId));
    }

    pageAllocationMap[pageId] = true;
}

uint32_t DiskManager::allocatePage() {
    
            // Reserve page 0 as "no page" sentinel; real pages start from 1
            uint32_t pageId = 1;
    while (pageAllocationMap.find(pageId) != pageAllocationMap.end() &&
        pageAllocationMap[pageId]) {
        pageId++;
    }

    // Initialize the page with zeros
    std::array<char, PAGE_SIZE> emptyPage;
    emptyPage.fill(0);
    writePage(pageId, emptyPage.data());

    return pageId;
}

void DiskManager::deallocatePage(uint32_t pageId) {
    pageAllocationMap[pageId] = false;
}

// Ensure file size is sufficient to hold the specified page
void DiskManager::ensureFileSize(uint32_t pageId) {
    file.seekg(0, std::ios::end);
    size_t fileSize = file.tellg();
    size_t requiredSize = (pageId + 1) * PAGE_SIZE;

    if (fileSize < requiredSize) {
        // Extend file size to meet new page requirements
        file.close();
        file.open(dbFile, std::ios::out | std::ios::in | std::ios::binary);
        file.seekp(requiredSize - 1, std::ios::beg);
        file.put('\0');
        file.flush();
        file.close();
        file.open(dbFile, std::ios::in | std::ios::out | std::ios::binary);
    }
}